package com.ikingtech.framework.sdk.authenticate.embedded.authenticate;

import com.ikingtech.framework.sdk.authenticate.embedded.core.Credential;
import com.ikingtech.framework.sdk.authenticate.embedded.core.UserIdentityLoader;
import com.ikingtech.framework.sdk.authenticate.embedded.propertires.AuthenticateProperties;
import com.ikingtech.framework.sdk.authenticate.extension.IdentityExtensionLoader;
import com.ikingtech.framework.sdk.context.security.Identity;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.enums.authenticate.SignEndpointTypeEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

/**
 * @author tie yan
 */
public class TokenAuthenticate extends AbstractAuthenticate {

	private final UserIdentityLoader userIdentityLoader;

	private final AuthenticateProperties properties;

	public TokenAuthenticate(StringRedisTemplate redisTemplate,
							 List<IdentityExtensionLoader> loaders,
							 UserIdentityLoader userIdentityLoader,
							 AuthenticateProperties properties) {
		super(redisTemplate, loaders);
		this.userIdentityLoader = userIdentityLoader;
		this.properties = properties;
	}

	@Override
	public Identity doVerify(Credential credential, String token) {
		String username = credential.getCredentialName();
		if (null != this.properties.getOauth2Sso() &&
				Boolean.TRUE.equals(this.properties.getOauth2Sso().getEnabled())) {
			String tokenRequestResultStr = Tools.Http.postForm(this.properties.getOauth2Sso().getTokenEndpoint() + "?" + Tools.Http.toQueryStr(Tools.Coll.newMap(
					Tools.Coll.Kv.of("grant_type", "authorization_code"),
					Tools.Coll.Kv.of("code", credential.getAuthorizeCode()),
					Tools.Coll.Kv.of("redirect_uri", URLEncoder.encode(this.properties.getOauth2Sso().getRedirectUri(), Charset.defaultCharset()))
			)), Tools.Str.EMPTY);
			Map<String, Object> tokenRequestResult = Tools.Json.toMap(tokenRequestResultStr);
			if (tokenRequestResult.containsKey("access_token") ||
					null == tokenRequestResult.get("access_token") ||
					Tools.Str.isBlank((String) tokenRequestResult.get("access_token"))) {
				throw new FrameworkException("loginFail");
			}
			String tokenIntrospectResultStr = Tools.Http.post(this.properties.getOauth2Sso().getUserInfoEndpoint() + "?" + "access_token=" + tokenRequestResult.get("access_token"), Tools.Str.EMPTY);
			username = this.getUsername(tokenIntrospectResultStr, Tools.Str.split(this.properties.getOauth2Sso().getUsernameField()));
		}
		Identity result = this.userIdentityLoader.loadByCredential(username);
		if (result == null) {
			throw new FrameworkException("loginFail");
		}
		this.resolveMultiSign(result.getId(), SignEndpointTypeEnum.PC.name());
		result.setEndpoint(SignEndpointTypeEnum.PC.name());
		result.setToken(credential.getToken());

		return result;
	}

	private String getUsername(Object tokenIntrospectResultStr, List<String> usernameFields) {
		Map<String, Object> resultMap = Tools.Json.objToMap(tokenIntrospectResultStr);
		String username = Tools.Str.EMPTY;
		for (String usernameField : usernameFields) {
			if (resultMap.containsKey(usernameField) &&
					null != resultMap.get(usernameField)) {
				if (resultMap.get(usernameField) instanceof String) {
					username = (String) resultMap.get(usernameField);
				} else {
					resultMap = Tools.Json.objToMap(resultMap.get(usernameField));
				}
			}
		}
		return username;
	}

	@Override
	public Boolean support(Credential credential) {
		return Tools.Str.isNotBlank(credential.getCredentialName()) && (Tools.Str.isNotBlank(credential.getToken()) || Tools.Str.isNotBlank(credential.getAuthorizeCode()));
	}
}
